Complete this lab

Introduction

The purpose of this lab is to review the following basic Java concepts that should have been covered in your previous courses:

This lab also introduces code testing using JUnit. This lab will be graded for style and correctness.

Before you begin

The API for the class that you need to implement can be found here.

A local copy of the Java API can be found here.

Style Rules

The style rules are not overly restrictive in EECS2030 but it is important that you follow them; we use a style checker for all of your submitted lab work. Most jobs that involve programming will require their employees to follow a rigid set of style rules.

1. Your programs should use the normal Java conventions (class names begin with an uppercase letter, variable names begin with a lowercase letter, public static final constants should be in all caps, etc.).


2. In general, use short but descriptive variable names. There are exceptions to this rule; for example, traditional loop variables are often called i, j, k, etc.

Avoid very long names; they are hard to read, take up too much screen space, and are easy to mistype.


3. Use a consistent indentation size. Beware of the TAB vs SPACE problem: Tabs have no fixed size; one editor might interpret a tab to be 4 spaces and another might use 8 spaces. If you mix tabs and spaces, you will have indenting errors when your code is viewed in different editors.


4. Use a consistent brace style:

// left aligned braces

class X
{
  public void someMethod()
  {
    // ...
  }
  
  public void anotherMethod()
  {
    for (int i = 0; i < 1; i++)
    {
      // ...
    }
  }
}

or

// ragged braces

class X {
  public void someMethod() {
    // ...
  }
  
  public void anotherMethod() {
    for (int i = 0; i < 1; i++) {
      // ...
    }
  }
}


5. Insert a space around operators (except the period ".").

The following

    // some code somewhere
    boolean isBetween = (x > MIN_VALUE) && (x > MAX_VALUE);
    int someValue = x + y * z;

is much easier to read than this

    // AVOID DOING THIS
    
    // some code somewhere
    boolean isBetween=(x>MIN_VALUE)&&(x>MAX_VALUE);
    int someValue=x+y*z;


6. Avoid using "magic numbers". A magic number is a number that appears in a program in place of a named constant. For example, consider the following code:

int n = 7 * 24;

What do the numbers 7 and 24 mean? Compare the code above to the following:

final int DAYS_PER_WEEK = 7;
final int HOURS_PER_DAY = 24;
int n = DAYS_PER_WEEK * HOURS_PER_DAY;

In the second example, the meaning of 7 and 24 is now clear (better yet would be to also rename n).

Not all numbers are magic numbers. You can usually use the values 0, 1, and 2 without creating a named constant. If you ever find yourself doing something like:

final int TEN = 10;

then you are probably better off using 10 and explaining its meaning in a comment.


7. A good IDE (integrated development environment) such as eclipse will correct many style errors for you. In eclipse, you can select the code that you want to format, right click to bring up a context menu, and choose Source -> Format to automatically format your code.

Getting started

These instructions assume that you have completed the previous parts of this lab, are working in the Prism lab, and have eclipse running.


In this lab, you will import an existing project rather than starting everything from scratch. In the eclipse File menu, choose the Import... menu item.


In the Import dialog box that appears, choose the Existing Projects into Workspace item and click Next:


If you are working on a Prism lab computer:
Click on the Select archive file radio button. Click on the Browse... button and select the file /eecs/dept/www/course/2030/labs/lab0/lab0_project.zip. Click the Finish button to import the project:


If you are NOT working on a Prism lab computer:
Download the following zip file . Click on the Select archive file radio button. Click on the Browse... button and select the file that you just downloaded. Click the Finish button to import the project.


On the left-hand side of the eclipse window, you will see a tab labelled Package Explorer. Use the small triangles to expand the lab0 contents, then the src contents, and finally the eecs2030.lab0 contents. Double-click on Lab0.java and TestLab0.java to open these files in the editor:


Click on the Lab0.java tab. Lab0.java is the Java source code file that you need to edit to complete this lab. It contains several methods that you should be able to complete if you have mastered the material from your previous Java programming course or courses.

Find the method with the header public static int maxInt(). Examining the the contract for the method, or reading the Javadoc comment preceding the method, tells us that the method should return the greatest value that can be represented by the type int. Examining the body of the method in eclipse, we see that the method is implemented like so:

public static int maxInt() {
    return 0;
}

which is clearly incorrect. DON'T FIX THE METHOD YET; continue following the lab document.


TestLab0.java is a test class that contains unit tests for all of the methods that you will implement in this lab. You will learn more about unit tests in your next lecture. For now, all you need to know is that you can use the test class to check for errors in the methods in Lab0.java.

Click on the TestLab0.java tab in the editor window to view the contents of TestLab0.java. Run the test class by pressing the green run button indicated by the red arrow in the figure below:

The results of running the tests are shown to you in the JUnit tab located on the left-hand side of the eclipse window (see figure above). Notice that all of the tests have a blue x beside them; the blue x indicates that the test has failed. In the Failure Trace panel, some diagnostic information is shown to you. For the test01_maxInt test, the diagnostic information is indicating that the test expected a value of 2147483647 but received a value of 0. It seems like there is something wrong with our implementation of the maxInt method.


Click on the Lab0.java tab in the editor window to view the contents of Lab0.java. Scroll down to the maxInt method (the first method in the class). Edit the return value of the method so that it returns the correct value as shown below:

Save the edited file (use the File menu or type Ctrl-s).


Click on the TestLab0.java tab in the editor window to view the contents of TestLab0.java. Re-run the test class; you should see the following:

Notice that the test test01_maxInt now has a green check mark beside it indicating that the test has passed. Unfortunately, the remaining tests are still failing.

In the remainder of this lab, you will use the test class to help you fix the remaining methods in the Lab0 class. Follow the remainder of the lab to review some fundamentals of the Java language and complete the exercises in the pink sections. While there appears to be a great deal of work, many of the methods can be completed with a single line of code.

Primitive types

In Java, all values have a type. A type defines a set of values and the operations that can be performed using those values.

Java's primitive types are those types that are predefined by the Java language and are named by a reserved keyword. The primitive types are all numeric types and one type representing true/false values.

int

The int type represents integer values in the range -2 31 to (2 31 - 1). Java will interpret any number not having a decimal as being an int value.

The following is an example of a (not very useful) method that always returns the value of 1. The method creates an int variable named result, assigns the variable a value of 1, and returns the value of the variable:

  public static int one() {
    int result = 1;
    return result;
  }

Constant values important to the int type can be found in the class java.lang.Integer .

double

The double type represents real values in the approximate range of -1.7 × 10 308 to 1.7 × 10 308. Java will interpret any number having a decimal as being a double value.

The following is an example of a (not very useful) method that always returns the value of 0.5. The method creates a double variable named result, assigns the variable a value of 0.5, and returns the value of the variable:

  public static double oneHalf() {
    double result = 0.5;
    return result;
  }

Constant values important to the double type can be found in the class java.lang.Double .

Exercise #1

Complete the method minDouble(). Don't forget that you can always consult the API to read the documentation for all of the methods.

Run the JUnit tester after you complete each method to check your work.

Arithmetic

Java provides several operators for performing arithmetic operations using int and double values:

Assume that you have the following declarations for each example in the table below:

int x = 14;
int y = -7;
double u = 2.5;
double v = -0.9;
Operator Name Example Value
+ unary plus operator
+x
+y
+u
+v

14
-7
2.5
-0.9
- unary minus operator
-x
-y
-u
-v

-14
7
-2.5
0.9
* multiplication operator
x * y
u * v

-98
-2.25
/ division operator
x / y
u / v

-2
-2.7777777777777777
% remainder (after division) operator
x % y
u % v

0
0.7
+ addition operator
x + y
u + v

7
1.6
- subtraction operator
x - y
u - v

21
3.4


Note that there is no exponentiation operator.

Java arithmetic obeys the same order of operations as regular arithmetic. Parentheses can be used in the same way as regular arithmetic to control the order of operations. For example, the following method computes the value of 11+x

  public static double f_of_x(double x) {
    double result = 1.0 / (1.0 + x);
    return result;
  }

int division

Any arithmetic operation involving two int values always produces another int value. Dividing two int values in Java produces the same result as dividing the two values as real numbers and discarding the fractional part of the result; an exception is thrown when dividing by 0:

Expression True Division Java Division
6 / 2 3 3
7 / 3 2.33... 2
1 / 2 0.5 0
-9 / 5 -1.8 -1
3 / 0 ArithmeticException

A common usage of int division is when you want to evenly distribute a number of whole items between a number of groups.

  /**
   * Returns the number of whole items each person receives
   * after distributing nItems evenly between nPeople.
   */
  public static int perPerson(int nItems, int nPeople) {
    int result = nItems / nPeople;
    return result;
  }

int remainder

Java provides the remainder after division operator %. For two int values x and y, the value of x % y is the int value equal to the remainder after dividing x by y:

Expression Java result
6 % 2 0
7 % 3 1
1 % 2 1
-9 % 5 -4
9 % -5 4
3 % 0 ArithmeticException

The sign of the result is defined to be equal to the sign of the first operand (the value to the left of the % operator).

A common usage of int remainder is when you want to evenly distribute a number of whole items between a number of groups.

  /**
   * Returns the number of whole items remaining
   * after distributing nItems evenly between nPeople.
   */
  public static int remainder(int nItems, int nPeople) {
    int result = nItems % nPeople;
    return result;
  }

Exercise #2

Complete the methods removeLastTwoDigits(int n) and lastTwoDigits(int n).

Note that in removeLastTwoDigits(int n) and lastTwoDigits(int n) you do not need to create named constants for any magic numbers you might need.

removeLastTwoDigits(int n) can be completed using integer division. lastTwoDigits(int n) can be completed using integer remainder. You do not (and should not) need to use an if statement in either method.

Run the JUnit tester after you complete each method to check your work.

Mixing int and double

Be careful when using int values to compute a double value. For example, suppose that you compute the fractional value one-third like so:

  double oneThird = 1 / 3;    // results in 0.0

Java uses the types of the operands to determine what version of an operator to use. In this case, the 1 and the 3 are both int literals; therefore, Java uses int division to compute the value of 1 / 3 before converting the computed value to double and assigning it to oneThird.

The solution is convert one or both of the operands to double to force Java to use floating-point division (the type of division performed when at least one of the operands has type double or float); any of the following will work:

  double oneThird = 1.0 / 3;
  double oneThird = 1 / 3.0;
  double oneThird = 1.0 / 3.0;
  double oneThird = (0.0 + 1) / 3;
  double oneThird = (double) 1 / 3;

Exercise #3

Complete the method avg(int a, int b). Be aware of the possibility of incorrectly using integer division instead of floating-point division.

Run the JUnit tester after you complete each method to check your work.

java.lang.Math

Mathematical functions such as exponentiation, trigonometric functions, roots, and others are provided by methods in the java.lang.Math class. This class also provides mathematical constants such as e and π and many other useful methods relating to basic mathematics operations.

Humidex is an index to indicate how hot the weather feels to the average person when there is some humidity. For example, if the temperature on a humid day is 30 degrees and the humidex is 40 then it means that it feels similar to a dry day where the temperature is 40 degrees. The Humidex formula used by Environment Canada when the air temperature in degrees Celcius is Ta and the dewpoint temperature in degrees Celcius is Td is:

Ta+0.5555(6.11e5417.7530(1273.161Td+273.16)10)

Observe that the implementation uses named constants instead of plain magic numbers to maintain good readability. If we knew what the various constants represented then we would have chosen more descriptive names for the constants.

  public static double humidex(double airTemp, double dewPoint) {
    final double A = 0.5555;
    final double B = 6.11;
    final double C = 5417.7530;
    final double D = 273.16;
    final double E = 10.0;
    
    double power = C * (1 / D - 1 / (dewPoint + D));
    return airTemp + A * (B * Math.exp(power) - E);
  }

Exercise #4

Environment Canada describes how to compute a value called the wind chill. The wind chill is an index that indicates how cold the weather feels to the average person when there is some wind. For example, if the air temperature is -5 degrees Celcius and the wind chill is -15 then it means that it feels similar to a windless day where the temperature is -15 degrees Celcius. The formula for computing wind chill W when the air temperature T is 0 degrees or less and the wind speed V is 5 km/h or greater is:

W=13.12+0.6215T+(0.3965T11.37)V0.16

Complete the method windChill(double airTemp, double windSpeed) using named constants to represent the magic numbers in the formula. The method that performs exponentiation is Math.pow. The API for the Math class can be found here.

Comparing primitive values

Java's primitive type for representing values that can be true or false is named boolean. One way to generate a boolean value is to compare two numeric values. Java provides the following equality and comparison operators for numeric values:

Expression Meaning
x == y is the value of x equal to the value of y
x != y is the value of x not equal to the value of y
x < y is the value of x less than the value of y
x > y is the value of x greater than the value of y
x <= y is the value of x less than or equal to the value of y
x >= y is the value of x greater than or equal to the value of y

An integer is even if the remainder after dividing by 2 is equal to 0. A method that determines if an integer is even could be implemented like so:

  public static boolean isEven(int x) {
    int remainder = x % 2;
    boolean result = (remainder == 0);  // is remainder equal to zero?
    return result;
  }

Exercise #5

Complete the methods isOdd(int x) and isOnUnitCircle(double x, double y).

Recall that the unit circle is defined as being the set of all points having coordinates (x,y) that satisfy the equation x2+y2=1.

Run the JUnit tester after you complete each method to check your work.

Boolean expressions

boolean values can be combined to produce a new boolean value using boolean algebra. In boolean algebra, the basic operations are AND, OR, and NOT, and the corresponding Java operators are:

boolean operation Java operator
AND &&
OR ||
NOT !

Use the AND operator && to determine if two boolean values are both true. For example, if you want to determine if a number x is greater than both y and z you could write:

  boolean result = (x > y) && (x > z);    // is x greater than y and is x greater than z

The parentheses are not necessary because && has lower precendence than the comparison operators, but are included in the example to help readability.

Use the OR operator || to determine if at least one of two boolean values are both true. For example, if you want to determine if a number x is greater than either y or z you could write:

  boolean result = (x > y) || (x > z);    // is x greater than y or is x greater than z

Again, the parentheses are not necessary.

The NOT operator ! negates a boolean value. Consider the following:

  boolean isEqual = (x == y);
  boolean isNotEqual = !isEqual;

In the example, isEqual is true if x and y have the same value, and false otherwise. The value of isNotEqual is true if x and y have the different values, and false otherwise.

Exercise #6

Complete the methods isInsideUnitSquare(double x, double y) and isOutsideUnitSquare(double x, double y).

Run the JUnit tester after you complete each method to check your work.

if statement

Java's if statement lets you choose to execute a block of code depending on a boolean value. For example, the following method uses an if statement to validate an input to the method:

  public static double circleCircumference(double radius) {
    if (radius < 0.0) {
      radius = -radius;     // make the radius positive
    }
    return 2.0 * Math.PI * radius;
  }

The method in the above example computes the circumference of a circle given a radius. The method first checks if the value of radius is negative; if radius is negative, the code inside the block of the if statement is run (shown in red). The code inside the block simply converts the value of radius to the correct positive value. If radius is not negative, then the block in red does not run. Whether or not the block in red runs, the value of the circumference is computed and returned.

Instead of correcting the negative radius value, we might have chosen to indicate that an error has occurred by throwing an exception:

  public static double circleArea(double radius) {
    if (radius < 0.0) {
      throw new IllegalArgumentException("negative radius");
      // method stops running here because an exception was thrown
    }
    return Math.PI * radius * radius;
  }

The method in the above example computes the area of a circle given a radius. The method first checks if the value of radius is negative; if radius is negative, the code inside the block of the if statement is run (shown in red). The code inside the block creates an IllegalArgumentException object and throws the exception. Throwing the exception causes the method to stop running immediately. If radius is not negative, then the block in red does not run, and the area is computed and returned.

Exercise #7

Complete the method flipBit(int). Note that the method can throw a BadBitException which is an exception class that has been included for you in the lab project.

A bit is the digit used in the binary number system. A bit can have a value of zero or one (with no other value allowed). Flipping a bit changes the value of the bit to the other value; flipping a bit that is zero changes it to one, and flipping a bit that is one changes it to zero.

Run the JUnit tester after you complete each method to check your work.

if-else statement

An if-else statement causes exactly one of two separate blocks of code to run depending on a boolean value. For example, the following method computes the minimum of two values using an if-else statement:

  public static int min2(int x, int y) {
    int min;
    if (y < x) {
      min = y;
    }
    else {
      min = x;
    }
    return min;
  }

In the above example, the red block is run if the value of y is less than the value of x; otherwise, the block in blue is run. After the red or blue block runs, the value of min is returned.

Exercise #8

Complete the method contains(double x, Range range). Remember to consult the API for Range to see how to use a range object.

Run the JUnit tester after you complete each method to check your work.

Chained if-else statement

In situations where exactly one of several blocks of code must run, you can chain multiple if-else statements together:

  public static int contains2(double x, Range range) {
    int result;
    if (x <= range.getMinimum()) {
      result = -1;
    }
    else if (x >= range.getMaximum()) {
      result = 1;
    }
    else {
      result = 0;
    }
    return result;
  }

The example code above determines if a value x lies to the left of a Range, the right of a Range, or is strictly inside the Range. The red block is run if x is less than or equal to the minimum value of the range, otherwise the blue block is run if x is greater than or equal to the maximum value of the range, otherwise the purple block is run. After the red, blue, or purple block runs, the value of result is returned.

Exercise #9

Complete the method compareTo(Range r1, Range r2). Try to use a chained if-else statement in your implementation.

Run the JUnit tester after you complete each method to check your work.

Strings

A String is a sequence of zero or more characters. A String can be written as a sequence of zero or more characters enclosed by quotation marks:

String s = "";           // the empty string
String t = "Hello";

The following method returns the title of this course as a String:

  public static String getCourse() {
    return "EECS2030";
  }

An important feature of Java strings is that once you create a String instance it is impossible to change its sequence of characters. This is because the String class provides no mutator methods (methods that mutate, or modify, the state of a String instance). If you need to change the sequence of characters of a String, you must create a new String instance.

Exercise #10

Complete the method getCourseName().

To complete this method, you should first consult the API for Lab0 to find the constant that contains the course name. You should then simply return the constant to complete the method; in other words, you should not duplicate string containing the course name in your method.

Run the JUnit tester after you complete each method to check your work.

String concatenation

Two String instances can be joined, or concatenated, using the + operator; this results in a new String instance. For example, the string "Hello, world" could be created by concatenating three strings like so:

  String u = "Hello" + ", " + "world";

The following method returns the title of this course and the course name separated by a space:

  public static String getCourseWithName() {
    return "EECS2030" + " " + Lab0.getCourseName(); 
  }

The following method returns the title of this course and the course name separated by a user-defined separator String:

  public static String getCourseWithName(String separator) {
    return "EECS2030" + separator + Lab0.getCourseName(); 
  }

Exercise #11

Complete the method toString(Range r). The API for the method defines what the string returned by the method should be equal to.

Run the JUnit tester after you complete each method to check your work.

String length and indexing

Individual characters can be retrieved from a String using an integer index. The first character has an index of 0, the second character has an index of 1, and so on. The last character of a String having n characters is (n - 1). For example, the characters of the string "abcd" have the following indices:

Index Character
0 'a'
1 'b'
2 'c'
3 'd'

The String method charAt(int index) returns the character at the given index. The following method returns the first character of a non-empty string:

  public static char firstChar(String s) {
    if (s.isEmpty()) {
      throw new IllegalArgumentException("string has length 0");
    }
    return s.charAt(0);
  }

The String method length() returns the length (number of characters) of a string. The following method returns the last character of a non-empty string:

  public static char lastChar(String s) {
    if (s.isEmpty()) {
      throw new IllegalArgumentException("string has length 0");
    }
    return s.charAt(s.length() - 1);
  }

Exercise #12

Complete the method middleChar(String s). The API for the method defines what the middle character is for a string with even length.

Run the JUnit tester after you complete each method to check your work.

for loops

A for loop is used when you want to repeat a block of code a known number of times. For example, the following method returns the string formed by concatenating a specified string n times:

  /**
   * Returns the string formed by concatenating the string s n times.
   * For illustration purposes only; you should use a StringBuilder 
   * instead of repeated concatenation.
   */
  public static String repeat(String s, int n) {
    String result = "";
    for (int i = 0; i < n; i++) {
        result = result + s;
    }
    return result;
  }

The for statement has three parts:

In the above example, the:

If you trace through the above loop using a list of n elements, you'll see that the value of the loop variable i follows the sequence 0, 1, 2, ..., (n - 1).

Exercise #13

Complete the method alternatingCaps(String s). The API for the method defines what the method should return and contains several examples.

To convert characters to lowercase, use the method Character.toLowerCase. To convert characters to uppercase, use the method Character.toUpperCase. The API for the Character class can be found here.

Run the JUnit tester after you complete each method to check your work.

Submit your work

In a terminal, find the directory containing your Lab0.java file. If you have been following all of the steps in this lab in sequence then you should be able to type the following into a terminal:

cd
cd eecs2030
cd EECS2030_Lab0/src/eecs2030/lab0

Recall that:

  1. cd on its own changes the current directory to your home directory
  2. cd eecs2030 changes the current directory to the workspace directory you created for HelloWorld.java
  3. cd EECS2030_Lab0/src/eecs2030/lab0 changes the current directory to the lab0 package for the project EECS2030_Lab0

Assuming that the above works, you can submit your work by typing:

submit 2030 lab0 Lab0.java

which submits your Lab0.java file. If your file is successfully submitted, then you will see the message All files successfully submitted and nothing else. If something is wrong with your submission, you will see a detailed error message explaining what is wrong with your submission.

If you want to check which files you have submitted you can use the command:

submit -l 2030 lab0

where -l is the hyphen followed by a lowercase ell (not the digit one). Lab 0 is complete once you have submitted HelloWorld.java and Lab0.java